package com.neo.service;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.cron.CronUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.neo.enums.DevJobStatusEnum;
import com.neo.mapper.DevJobMapper;
import com.neo.model.DevJob;
import com.neo.util.CronExpressionUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class DevJobServiceImpl extends ServiceImpl<DevJobMapper, DevJob>
        implements DevJobService{


    @Override
    public Boolean add(DevJob devJob) {
        checkParam(devJob);
        devJob.setCode(RandomUtil.randomString(10));
        devJob.setJobStatus(DevJobStatusEnum.STOPPED.getValue());
        return this.save(devJob);
    }

    @Override
    public void edit(DevJob devJobEditParam) {
        DevJob devJob = this.getById(devJobEditParam.getId());
        if(devJob.getJobStatus().equals(DevJobStatusEnum.RUNNING.getValue())) {
            throw new RuntimeException("运行中的定时任务不可编辑，id值为：");
        }
        checkParam(devJobEditParam);
        BeanUtil.copyProperties(devJobEditParam, devJob);
        this.updateById(devJob);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(List<DevJob> devJobIdParamList) {
        List<String> devJobIdList = CollStreamUtil.toList(devJobIdParamList, DevJob::getId);
        if(ObjectUtil.isNotEmpty(devJobIdList)) {
            // 将运行中的停止
            devJobIdList.forEach(CronUtil::remove);
            // 执行删除
            this.removeByIds(devJobIdList);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void stopJob(DevJob devJobIdParam) {
        DevJob devJob = this.getById(devJobIdParam.getId());
        if(devJob.getJobStatus().equals(DevJobStatusEnum.STOPPED.getValue())) {
            throw new RuntimeException("定时任务已经处于停止状态，id值为：");
        }
        // 将运行中的定时任务停止
        CronUtil.remove(devJob.getId());
        this.update(new LambdaUpdateWrapper<DevJob>().eq(DevJob::getId, devJobIdParam.getId())
                .set(DevJob::getJobStatus, DevJobStatusEnum.STOPPED.getValue()));
    }
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void runJob(DevJob devJobIdParam) {
        DevJob devJob = this.getById(devJobIdParam.getId());
        if(devJob.getJobStatus().equals(DevJobStatusEnum.RUNNING.getValue())) {
            throw new RuntimeException("定时任务已经处于运行状态，id值为");
        }
        CronUtil.schedule(devJob.getId(), devJob.getCronExpression(), () -> {
            try {
                // 运行定时任务
                ((CommonTimerTaskRunner) SpringUtil.getBean(Class.forName(devJob.getActionClass()))).action();
            } catch (ClassNotFoundException e) {
                throw new RuntimeException("定时任务找不到对应的类，名称");
            }
        });
        this.update(new LambdaUpdateWrapper<DevJob>().eq(DevJob::getId, devJobIdParam.getId())
                .set(DevJob::getJobStatus, DevJobStatusEnum.RUNNING.getValue()));
    }

    @Override
    public void runJobNow(DevJob devJobIdParam) {
        DevJob devJob = this.getById(devJobIdParam.getId());
        if(devJob.getJobStatus().equals(DevJobStatusEnum.STOPPED.getValue())) {
            // 如果是停止的，则先开启运行
            this.runJob(devJobIdParam);
        }
        try {
            // 直接运行一次
            ((CommonTimerTaskRunner) SpringUtil.getBean(Class.forName(devJob.getActionClass()))).action();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("定时任务找不到对应的类，名称为");
        }
    }

    private void checkParam(DevJob devJobEditParam) {
        if(!CronExpressionUtil.isValidExpression(devJobEditParam.getCronExpression())) {
            //throw new RuntimeException(("cron表达式：{}格式不正确", devJobEditParam.getCronExpression()));
            throw new RuntimeException(("cron表达式格式不正确"));
        }
        try {
            Class<?> actionClass = Class.forName(devJobEditParam.getActionClass());
            if(!CommonTimerTaskRunner.class.isAssignableFrom(actionClass)) {
                List<String> actionClassArr = StrUtil.split(devJobEditParam.getActionClass(), StrUtil.DOT);
                //throw new CommonException("定时任务对应的类：{}不符合要求", actionClassArr.get(actionClassArr.size() - 1));
                throw new RuntimeException("定时任务对应的类不符合要求");
            }
        } catch (ClassNotFoundException e) {
            //throw new CommonException("定时任务找不到对应的类，名称为：{}", devJobEditParam.getActionClass());
            throw new RuntimeException("定时任务找不到对应的类名称");
        }
        boolean hasSameJob = this.count(new LambdaQueryWrapper<DevJob>()
                .eq(DevJob::getActionClass, devJobEditParam.getActionClass())
                .eq(DevJob::getCronExpression, devJobEditParam.getCronExpression())
                .ne(DevJob::getId, devJobEditParam.getId())) > 0;
        if (hasSameJob) {
            //throw new CommonException("存在重复的定时任务，名称为：{}", devJobEditParam.getName());
            throw new RuntimeException("存在重复的定时任务");
        }
    }

    @Override
    public List<DevJob> getlist(DevJob devJob) {
        return this.list();
    }

    @Override
    public List<String> getActionClass() {
        Map<String, CommonTimerTaskRunner> commonTimerTaskRunnerMap = SpringUtil.getBeansOfType(CommonTimerTaskRunner.class);
        if (ObjectUtil.isNotEmpty(commonTimerTaskRunnerMap)) {
            Collection<CommonTimerTaskRunner> values = commonTimerTaskRunnerMap.values();
            return values.stream().map(commonTimerTaskRunner -> commonTimerTaskRunner.getClass().getName()).collect(Collectors.toList());
        } else {
            return CollectionUtil.newArrayList();
        }
    }
}



